/*
 * (C) 2009, Elephant Seven Hamburg Gmbh, 
 * Author: Detlev Wiese
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 
 * use this file except in compliance with the License. You may obtain a copy 
 * of the License at
 *  
 *     http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.se and its accompanying notice file is
 * included with the distribution. 
 */ 
package com.e7.streams;

import java.util.NoSuchElementException;

/**
 * <P>A stream which delivers its elements up to a certain 
 * boolean condition.</P>
 * <P>All stream-elements have to fulfill a {@link Predicate}. 
 * When the first element is found not matching the predicate, 
 * the stream will stop to deliver further elements.</P>   
 * @param <E> The type of the underlying stream.
 */
public class LimitedStream<E> extends AbstractStream<E> 
	implements Stream<E> 
{
	private Predicate<E> predicate; 
	private Stream<E> stream; 
	
	// element computed in hasNext()
	private E current; 
	// true, if the predicate did match for the first time
	private boolean limitReached = false; 
	// true, if the element computed has been delivered.
	private boolean delivered = true;  
	
	
	/**
	 * Create an instance!
	 * @param predicate The predicate to use for limiting delivery 
	 * @param stream The underlying stream to deliver
	 */
	public LimitedStream(Predicate<E> predicate, Stream<E> stream) { 
		this.predicate = predicate; 
		this.stream = stream;
	}
	
	/**
	 * Syntactic sugar only.
	 * See {@link LimitedStream#LimitedStream(Predicate, Stream)}. 
	 */
	public LimitedStream(Stream<E> stream, Predicate<E> predicate) {
		this(predicate, stream); 
	}
	
	public boolean hasNext() {
		if (limitReached)
			return false; 
		
		if (!delivered) 
			return true; 
		
		if (stream.hasNext()) {
			current = stream.next();
			delivered = false; 
			limitReached = predicate.value(current); 
		} else 
			return false; 
		
		return !limitReached;
	}

	public E next() {
		if (this.hasNext()) {
			delivered = true; 
			return current; 
		} else 
			throw new NoSuchElementException();
	}
}
